#pragma once

/*
    日志落地模块
    作用:负责将格式化好的字符写入到不同位置(1:标准输出,2:文件,3:异步文件(按照时间/大小更新文件))

*/
#include <iostream>
#include <fstream>
#include <sstream>
#include "Util.hpp"
#include <cassert>
#include <memory>

namespace log
{
    // 抽象类:是为了更好的对写入不同位置类进行管理
    class LogTo
    {
    public:
        using ptr = std::shared_ptr<LogTo>;
        // 为了方便给我们自己定义的子类资源进行释放
        virtual ~LogTo()
        {
        }
        virtual void WriteLog(const char *str, size_t len) = 0;
    };
    // 日志到标准输出
    class LogToStdout : public LogTo
    {
    public:
        void WriteLog(const char *str, size_t len)
        {
            std::cout.write(str, len);
        }
    };
    class LogToFile : public LogTo
    {
    public:
        LogToFile(const std::string &pathname) : _PathName(pathname)
        {
            // 1:创建文件所在的目录
            log::util::File::Mkdir(log::util::File::GetFileDirectory(_PathName));
            // 2:打开文件
            _ofs.open(_PathName, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void WriteLog(const char *str, size_t len)
        {
            
            _ofs.write(str, len);
            assert(_ofs.good()); // 检查写入是否成功
        }

    private:
        std::string _PathName; // 写入文件的路径
        std::ofstream _ofs;    // 打开文件的操作句柄,防止每写一次打开文件一次
    };
    class LogToRollFileBySize : public LogTo
    {
    public:
        LogToRollFileBySize(const std::string &basename, size_t maxsize) : _basename(basename), _filemaxsize(maxsize), _cur_size(0),_count(0)
        {
            std::string pathname = CreateFile();
            // 1:创建文件所在的目录
            log::util::File::Mkdir(log::util::File::GetFileDirectory(pathname));
            // 2:打开文件
            _ofs.open(pathname, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        void WriteLog(const char *str, size_t len)
        {
            if (_cur_size >= _filemaxsize)
            {
                _ofs.close();
                std::string pathname = CreateFile();
                _cur_size=0;
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                assert(_ofs.is_open());
            }
            _ofs.write(str, len);
            assert(_ofs.good());
            _cur_size += len;
        }
        std::string CreateFile()
        {
            // 获取时间戳
            time_t timestamp = time(nullptr);
            struct tm t;
            localtime_r(&timestamp, &t); // 将时间戳转化为日历那样的时间格式
            std::string filename = _basename;
            std::stringstream ss;
            t.tm_year += 1900;
            t.tm_mon += 1;
            ss << t.tm_year << "-" << t.tm_mon << "-" << t.tm_mday << " [" << t.tm_hour << ":" << t.tm_min << ":" << t.tm_sec<<"] ";
            ss<<_count++;
            ss << ".log";
            filename += ss.str();
            return filename;
        }

    private:
        std::string _basename; // 滚动文件的名称
        size_t _filemaxsize;   // 一个文件的最大大小B
        std::ofstream _ofs;
        size_t _cur_size; // 现在文件的大小
        ssize_t _count;
    };
     
    // 简单工厂模式

    class SinkFactory
    {
    public:
        template <typename SinkType, typename... Args>
        static LogTo::ptr CreateSink(Args&& ...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...); 
        }
    };

}